11. 迭代器与生成器 Iterators & Generators
迭代器(Iterators) 是众多编程语言(例如 C++、Java、Python)中的常见接口,用于访问多种可迭代容器中的元素。在这些语言中,可迭代容器都需提供对应的迭代器,该迭代器按照某种顺序访问该容器中的元素。常见的可迭代元素有:列表、字典、range()。
迭代器是可变对象。
内置函数 iter(iterable) 与 next(iterator) 分别用于对指定的可迭代对象创建迭代器与返回某个迭代器指向的当前元素,并将该迭代器指向下一个元素。
list() 函数可以直接将迭代器中剩余的元素一次性迭代出来。
for 语句也可以接受迭代器作为迭代对象,但它同样只能遍历迭代器中剩余的元素。且遍历完后迭代器由于没有剩余元素无法再被使用。
迭代有着确定的方向。如果某迭代器已经迭代完成所有元素,要求该迭代器继续迭代元素将抛出 StopIteration 异常。
在 Python 中,不同的可迭代容器其迭代器迭代元素的顺序如下:
- 列表:索引顺序
- 字典:元素加入顺序(Python 3.6 后);
任意顺序(Python 3.5 前)
Python 中有许多内置的用于处理可迭代对象的函数。这些函数存在一个特征:它们是”懒“函数。调用这些函数会返回一个迭代器。只有使用该迭代器迭代到了对应的值,才会对该值应用对应的函数。例如:
map(func, iterable)返回一个迭代器,每当迭代器迭代一个值x时,返回func(x)的值。filter(func, iterable)与map类似。对迭代器迭代的值x应用谓词函数func,检查func(x)的值是否为真。zip(first_iter, second_iter, ...)返回一个包含所有相同索引的(x, y, ...)元组的迭代器。跳过某个可迭代对象多余的部分。reversed(sequence)返回一个逆序迭代序列所有元素的迭代器。
迭代器在使用时有一些注意事项:
- 不能对 大小 更新后的字典使用更新前的迭代器
迭代器的意义在于,将列表、元组、字典等可迭代对象置于迭代器的统一抽象之下,这允许我们对输入数据做更少假设,并使用一种更加通用方便的方式处理数据。同时,迭代器也限制了代码处理数据的方式,最典型的就是通过将可迭代对象封装为迭代器,可以保证其它代码使用该迭代器时只能至多访问对象中的每个元素一次。
生成器(Generators) 是一种特殊的迭代器,其由 生成器函数(Generator Function) 返回。生成器函数的一个显著特征是使用 yield 关键字生成函数的返回值,并将其装进调用函数时自动生成的生成器中。yield 关键字与 return 的最大不同在于执行 return 会导致函数 立刻结束并返回对应的值,而 yield 函数可以多次执行。
生成器函数是“懒”函数。即调用生成器函数时,函数只是返回一个生成器,函数体不执行。只有开始迭代返回的生成器时,函数题才会开始工作直到执行 yield 语句,返回迭代出的值后暂停执行等待后续迭代。
def generate_odds(start, end):
next = start + (1 - start % 2)
while next < end:
print(f"Generated Odd Number:{next}")
yield next
next += 2
>>> gen = generate_odds(0, 10)
>>> next(gen)
Generated Odd Number:1
1
>>> next(gen)
Generated Odd Number:3
3
>>> list(gen)
Generated Odd Number:5
Generated Odd Number:7
Generated Odd Number:9
[5, 7, 9]
由于生成器函数通常涉及到处理传入的迭代器,因此在 Python 3.3 后新增了 yield from 关键字,其可以接受一个迭代器或可迭代对象并将其作为生成器函数的返回值。等价于如下简写:
for i in iterable:
yield i
def countdown(t):
if t > 0:
yield t
yield from countdown(t-1)
>>> list(countdown(3))
3
2
1